package redis

import (
	"a-common/config"
	"context"
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/redis/go-redis/v9"
)

var (
	redisClientTemp *redisClient
	redisOnce       sync.Once
)

type redisClient struct {
	RedisCli *redis.Client
}

func NewRedisCli() *redisClient {
	redisOnce.Do(func() {
		conf := config.Config.Redis
		redisCli := redis.NewClient(&redis.Options{
			Addr:     conf.Url,
			Password: conf.Pwd,
			DB:       conf.Db,
		})
		_, err := redisCli.Ping(context.Background()).Result()
		if err != nil {
			panic(errors.New("redis initialization failed, " + err.Error()))
		}
		redisClientTemp = &redisClient{
			RedisCli: redisCli,
		}
	})
	return redisClientTemp
}

// 获取redis自增Id
func (rc *redisClient) Incr(key string, ttl int64) (int64, error) {
	id, err := rc.RedisCli.Incr(context.Background(), key).Result()
	if err != nil {
		return 0, err
	}

	// 设置Key过期时间
	if ttl != 0 {
		// 获取键的剩余过期时间
		t := rc.RedisCli.TTL(context.Background(), key).Val()
		// 设置过期时间
		if t == -1 {
			_, err = rc.RedisCli.Expire(context.Background(), key, time.Duration(ttl)*time.Second).Result()
			if err != nil {
				return 0, err
			}
		}
	}
	return id, nil
}

// 设置k-v
func (rc *redisClient) Set(key string, val any) error {
	return rc.RedisCli.Set(context.Background(), key, val, -1).Err()
}

// 设置k-v,及过期时间ttl, 单位秒
func (rc *redisClient) SetEx(key string, val any, ttl time.Duration) error {
	return rc.RedisCli.SetEx(context.Background(), key, val, ttl*time.Second).Err()
}

// 设置k-v,若key不存在，则设置val
func (rc *redisClient) SetNx(key string, ttl time.Duration, val any) error {
	return rc.RedisCli.SetNX(context.Background(), key, val, ttl*time.Second).Err()
}

// 通过key，获取val
func (rc *redisClient) Get(key string) (any, error) {
	return rc.RedisCli.Get(context.Background(), key).Result()
}

// 根据通配符获取key
func (rc *redisClient) Keys(pattern string) ([]string, error) {
	return rc.RedisCli.Keys(context.Background(), pattern).Result()
}

// 删除key,支持批量删除
func (rc *redisClient) Del(key ...string) error {
	return rc.RedisCli.Del(context.Background(), key...).Err()
}

// 获取key过期时间
func (rc *redisClient) GetExpire(key string) (time.Duration, error) {
	return rc.RedisCli.TTL(context.Background(), key).Result()
}

// 根据key和field字段设置，field字段的值
func (rc *redisClient) HSet(key string, field string, val any) error {
	return rc.RedisCli.HSet(context.Background(), key, field, val).Err()
}

// 根据key和多个字段名和字段值，批量设置hash字段值
func (rc *redisClient) HMSet(key string, data map[string]any) error {
	return rc.RedisCli.HMSet(context.Background(), key, data).Err()
}

// 如果field字段不存在，则设置hash字段值； 若存在则不设置
func (rc *redisClient) HSetNX(key string, field string, val any) error {
	return rc.RedisCli.HMSet(context.Background(), key, field, val).Err()
}

// 根据key和field字段，查询field字段的值
func (rc *redisClient) HGet(key string, field string) (any, error) {
	return rc.RedisCli.HGet(context.Background(), key, field).Result()
}

// 根据key查询所有字段和值
func (rc *redisClient) HGetAll(key string) (any, error) {
	return rc.RedisCli.HGetAll(context.Background(), key).Result()
}

// 根据key和field字段，累加数值
func (rc *redisClient) HIncrBy(key string, field string, val int64) (any, error) {
	return rc.RedisCli.HIncrBy(context.Background(), key, field, val).Result()
}

// 根据key返回所有字段名
func (rc *redisClient) HKeys(key string) (any, error) {
	return rc.RedisCli.HKeys(context.Background(), key).Result()
}

// 根据key，查询hash的字段数量
func (rc *redisClient) HLen(key string) (any, error) {
	return rc.RedisCli.HLen(context.Background(), key).Result()
}

// 根据key和多个字段名，批量查询多个hash字段值
func (rc *redisClient) HMGet(key string, fields ...string) (any, error) {
	return rc.RedisCli.HMGet(context.Background(), key, fields...).Result()
}

// 根据key和字段名，删除hash字段，支持批量删除hash字段
func (rc *redisClient) HDel(key string, fields ...string) error {
	return rc.RedisCli.HDel(context.Background(), key, fields...).Err()
}

// 检测hash字段名是否存在
func (rc *redisClient) HExists(key string, field string) error {
	return rc.RedisCli.HExists(context.Background(), key, field).Err()
}

// SAdd 添加集合元素
func (rc *redisClient) SAdd(key string, val ...any) error {
	return rc.RedisCli.SAdd(context.Background(), key, val...).Err()
}

// SCard 获取集合元素个数
func (rc *redisClient) SCard(key string) (int64, error) {
	return rc.RedisCli.SCard(context.Background(), key).Result()
}

// SIsMember 判断元素是否在集合中
func (rc *redisClient) SIsMember(key string, val any) (bool, error) {
	return rc.RedisCli.SIsMember(context.Background(), key, val).Result()
}

// SMembers 获取集合中所有的元素
func (rc *redisClient) SMembers(key string) ([]string, error) {
	return rc.RedisCli.SMembers(context.Background(), key).Result()
}

// SRem 删除集合中的元素
func (rc *redisClient) SRem(key string, val ...any) error {
	return rc.RedisCli.SRem(context.Background(), key, val...).Err()
}

// 随机返回集合中的元素，并且删除返回的元素
func (rc *redisClient) SPop(key string) error {
	return rc.RedisCli.SPop(context.Background(), key).Err()
}

// 随机返回集合中的多个元素，并且删除这些元素
func (rc *redisClient) SPopN(key string, n int64) error {
	return rc.RedisCli.SPopN(context.Background(), key, n).Err()
}

// 发布消息
func (rc *redisClient) Publish(ctx context.Context, channel string, msg string) error {
	var err error
	fmt.Println("Subscribe -> ", msg)
	err = rc.RedisCli.Publish(ctx, channel, msg).Err()
	return err
}

// 订阅消息
func (rc *redisClient) Subscribe(ctx context.Context, channel string) (string, error) {
	fmt.Println("ctx -> ", ctx)
	sub := rc.RedisCli.Subscribe(ctx, channel)
	msg, err := sub.ReceiveMessage(ctx)
	fmt.Println("Subscribe -> ", msg.Payload)
	return msg.Payload, err
}

// RPush 设置list
func (rc *redisClient) RPush(key string, values ...interface{}) error {
	return rc.RedisCli.RPush(context.Background(), key, values).Err()
}

// LRange 获取List
func (rc *redisClient) LRange(key string, start, stop int64) []any {
	return rc.RedisCli.LRange(context.Background(), key, start, stop).Args()
}

// LRange 获取List
func (rc *redisClient) LPop(key string) (string, error) {
	return rc.RedisCli.LPop(context.Background(), key).Result()
}
